package com.demo.camunda.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.demo.camunda.ReponseBO.HistoryTaskBO;
import com.demo.camunda.ReponseBO.ProcessInstanceRpBO;
import com.demo.camunda.camundaBO.ActivitiHighLineBO;
import com.demo.camunda.camundaBO.HistoricTaskInstanceEventBO;
import com.demo.camunda.camundaBO.TaskBO;
import com.demo.camunda.service.queryService;
import com.demo.camunda.util.PageUtil;
import com.demo.camunda.util.StaticValue;
import com.demo.camunda.util.cache.LocalCache;
import com.demo.camunda.variableBO.EvectionBO;
import org.apache.commons.lang3.StringUtils;
import org.camunda.bpm.engine.*;
import org.camunda.bpm.engine.history.HistoricActivityInstance;
import org.camunda.bpm.engine.history.HistoricProcessInstance;
import org.camunda.bpm.engine.history.HistoricTaskInstance;
import org.camunda.bpm.engine.history.HistoricVariableInstance;
import org.camunda.bpm.engine.impl.util.IoUtil;
import org.camunda.bpm.engine.repository.ProcessDefinition;
import org.camunda.bpm.engine.repository.ProcessDefinitionQuery;
import org.camunda.bpm.engine.rest.dto.repository.ProcessDefinitionDiagramDto;
import org.camunda.bpm.engine.rest.exception.InvalidRequestException;
import org.camunda.bpm.engine.rest.exception.RestException;
import org.camunda.bpm.engine.runtime.ProcessInstance;
import org.camunda.bpm.engine.task.Task;
import org.camunda.bpm.model.bpmn.BpmnModelInstance;
import org.camunda.bpm.model.bpmn.instance.FlowNode;
import org.camunda.bpm.model.bpmn.instance.SequenceFlow;
import org.camunda.bpm.model.xml.instance.DomElement;
import org.camunda.bpm.model.xml.instance.ModelElementInstance;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.ws.rs.core.Response;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class queryServiceImpl implements queryService {

    @Resource
    private RepositoryService repositoryService;
    @Resource
    private RuntimeService runtimeService;
    @Resource
    private ManagementService managementService;
    @Resource
    private TaskService taskService;
    @Resource
    private HistoryService historyService;

    @Resource
    private ProcessEngine processEngine;




    @Override
    public Map<String, Object> queryActivityInstance(int pageIndex) {

        //获取基本流程信息
        List<ProcessInstance> lists = runtimeService.createProcessInstanceQuery()
                .active()
                .list();
//        if(LocalCache.isContain(StaticValue.ProcessInstanceQuery)){
//            lists = (List<ProcessInstance>) LocalCache.get(StaticValue.ProcessInstanceQuery);
//        }else{
//            lists = runtimeService.createProcessInstanceQuery()
//                    .active()
//                    .list();
//            LocalCache.put(StaticValue.ProcessInstanceQuery,lists,1);
//        }


        if (CollectionUtil.isEmpty(lists)) {
            return new HashMap<>(0);
        }

        List<ProcessInstanceRpBO> processInstanceRpBOList = new ArrayList<>(16);
        lists.stream().forEach(o -> {
            ProcessInstanceRpBO processInstanceRpBO = new ProcessInstanceRpBO();
            processInstanceRpBO.setProcessDefinitionId(o.getProcessDefinitionId());
            processInstanceRpBO.setProcessInstanceId(o.getProcessInstanceId());
            processInstanceRpBO.setId(o.getId());
            processInstanceRpBO.setRootProcessInstanceId(o.getRootProcessInstanceId());
            processInstanceRpBO.setCaseInstanceId(o.getCaseInstanceId());
            processInstanceRpBO.setBusinessKey(o.getBusinessKey());
            processInstanceRpBO.setTenantId(o.getTenantId());
            processInstanceRpBOList.add(processInstanceRpBO);
        });
        ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery();
        processInstanceRpBOList.stream().forEach(o -> {
            o.setBpmnXml(getBpmn(o.getProcessDefinitionId()));
            //获取当前task任务审批人
            List<Task> list = taskService.createTaskQuery().active().processInstanceId(o.getProcessInstanceId()).list();
            List<TaskBO> listBO = new ArrayList<>();
            list.stream().forEach(a -> {
                TaskBO taskBO = new TaskBO();
                taskBO.setAssignee(a.getAssignee());
                taskBO.setName(a.getName());
                taskBO.setId(a.getId());
                taskBO.setTaskDefinitionKey(a.getTaskDefinitionKey());
                taskBO.setCreateTime(a.getCreateTime());
                taskBO.setProcessInstanceId(a.getProcessInstanceId());
                listBO.add(taskBO);
                if (a.getProcessInstanceId().equals(o.getProcessInstanceId())) {
                    o.setCreateTime(a.getCreateTime());
                }
            });
            o.setTaskBOList(listBO);
            if(CollectionUtil.isNotEmpty(o.getTaskBOList())){
                o.setState(o.getTaskBOList().get(0).getName());
            }

            //获取act_re_procdef 表中name_ 属性
            List<ProcessDefinition> processDefinitionList = query.processDefinitionId(o.getProcessDefinitionId()).list();


            List<HistoricVariableInstance> historicVariableInstances = historyService.createHistoricVariableInstanceQuery().processInstanceId(o.getProcessInstanceId()).list();
            historicVariableInstances.stream().forEach(l ->{
                if("evection".equals(l.getName())){
                    o.setEvectionBO((EvectionBO) l.getValue());
                }
            });

            if( o.getEvectionBO()!= null && StringUtils.isNoneBlank(o.getEvectionBO().getName()) ){
                o.setProcessInstanceName(o.getEvectionBO().getName()+"-"+processDefinitionList.get(0).getName());
            }else{
                o.setProcessInstanceName(processDefinitionList.get(0).getName());
            }


        });
        List<ProcessInstanceRpBO> collect = processInstanceRpBOList.stream().sorted(Comparator.comparing(ProcessInstanceRpBO::getCreateTime,Comparator.nullsLast(Date :: compareTo)).reversed()).collect(Collectors.toList());

        Map<String, Object> map = PageUtil.startPage(pageIndex, collect);
        return map;
    }

    private String getBpmn(String id) {
        InputStream processModelIn = null;
        try {
            processModelIn = repositoryService.getProcessModel(id);
            byte[] processModel = IoUtil.readInputStream(processModelIn, "processModelBpmn20Xml");
            ProcessDefinitionDiagramDto processDefinitionDiagramDto = ProcessDefinitionDiagramDto
                    .create(id, new String(processModel, "UTF-8"));
            String s = processDefinitionDiagramDto.getBpmn20Xml().replaceAll("[\t\n\r]", "");
            return s;
        } catch (AuthorizationException e) {
            throw e;
        } catch (ProcessEngineException e) {
            throw new InvalidRequestException(Response.Status.BAD_REQUEST,
                    e, "No matching definition with id " + id);
        } catch (UnsupportedEncodingException e) {
            throw new RestException(Response.Status.INTERNAL_SERVER_ERROR, e);
        } finally {
            IoUtil.closeSilently(processModelIn);
        }
    }

    @Override
    public Map<String, Object> queryHistoryInstance(int pageIndex) {
        List<HistoricTaskInstance> list  = historyService.createHistoricTaskInstanceQuery()
                .list();
//        if(LocalCache.isContain(StaticValue.HistoricTaskInstanceQuery)){
//             list = (List<HistoricTaskInstance>) LocalCache.get(StaticValue.HistoricTaskInstanceQuery);
//        }else{
//             list = historyService.createHistoricTaskInstanceQuery()
//                    .list();
//            LocalCache.put(StaticValue.HistoricTaskInstanceQuery,list,1);
//        }
        List<HistoricTaskInstanceEventBO> historicTaskInstanceEventBOList = new ArrayList<>(0);
        list.stream().forEach(o -> {
            HistoricTaskInstanceEventBO historicTaskInstanceEventBO = new HistoricTaskInstanceEventBO();
            historicTaskInstanceEventBO.setActivityInstanceId(o.getActivityInstanceId());
            historicTaskInstanceEventBO.setAssignee(o.getAssignee());
            historicTaskInstanceEventBO.setName(o.getName());
            historicTaskInstanceEventBO.setOwner(o.getOwner());
            historicTaskInstanceEventBO.setProcessInstanceId(o.getProcessInstanceId());
            historicTaskInstanceEventBO.setStartTime(o.getStartTime());
            historicTaskInstanceEventBO.setEndTime(o.getEndTime());
            historicTaskInstanceEventBO.setDeleteReason(o.getDeleteReason());
            historicTaskInstanceEventBO.setProcessDefinitionId(o.getProcessDefinitionId());
            historicTaskInstanceEventBOList.add(historicTaskInstanceEventBO);
        });

        List<HistoryTaskBO> taskBOList = new ArrayList<>();
        list.stream().collect(Collectors.collectingAndThen(
                Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(HistoricTaskInstance::getProcessInstanceId))), ArrayList::new))
                .forEach(oo -> {
            HistoryTaskBO historyTaskBO = new HistoryTaskBO();
            historyTaskBO.setProcessInstanceId(oo.getProcessInstanceId());
            historyTaskBO.setProcessDefinitionId(oo.getProcessDefinitionId());
            taskBOList.add(historyTaskBO);
        });



        ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
        taskBOList.stream().forEach(p -> {
            //获取act_re_procdef 表中name_ 属性
            List<ProcessDefinition> processDefinitionList =
                    processDefinitionQuery.processDefinitionId(p.getProcessDefinitionId()).list();

            p.setBpmn(getBpmn(p.getProcessDefinitionId()));
            List<HistoricVariableInstance> historicVariableInstances = historyService.createHistoricVariableInstanceQuery().processInstanceId(p.getProcessInstanceId()).list();
            historicVariableInstances.stream().forEach(l ->{
                if("evection".equals(l.getName())){
                    p.setEvectionBO((EvectionBO) l.getValue());
                }
            });

            if(  p.getEvectionBO()!= null && StringUtils.isNoneBlank(p.getEvectionBO().getName())){
                p.setProcessInstanceName(p.getEvectionBO().getName()+"-"+processDefinitionList.get(0).getName());
            }else{
                p.setProcessInstanceName(processDefinitionList.get(0).getName());
            }

        });

        Map<String, List<HistoricTaskInstanceEventBO>> map = historicTaskInstanceEventBOList.stream()
                .collect(Collectors.groupingBy(HistoricTaskInstanceEventBO::getProcessInstanceId));

        map.keySet().stream().forEach(m -> {
            taskBOList.stream().forEach(t -> {
                if (m.equals(t.getProcessInstanceId())) {
                    t.setList(map.get(m));
                }
            });
        });

        List<HistoricProcessInstance> historicProcessInstanceList  = historyService.createHistoricProcessInstanceQuery().list();
//        if(LocalCache.isContain(StaticValue.HistoricProcessInstanceQuery)){
//            historicProcessInstanceList = (List<HistoricProcessInstance>) LocalCache.get(StaticValue.HistoricProcessInstanceQuery);
//        }else{
//            historicProcessInstanceList = historyService.createHistoricProcessInstanceQuery().list();
//            LocalCache.put(StaticValue.HistoricProcessInstanceQuery,historicProcessInstanceList,1);
//        }


        //获取时间和状态
        taskBOList.stream().forEach(k ->{
            historicProcessInstanceList.stream().forEach( h ->{
                if(k.getProcessInstanceId().equals(h.getId())){
                    k.setStartTime(h.getStartTime());
                    k.setState(h.getState());
                }
            });
        });

        List<HistoryTaskBO> collect = taskBOList.stream().sorted(Comparator.comparing(HistoryTaskBO::getStartTime).reversed()).collect(Collectors.toList());


        Map<String, Object> stringObjectMap = PageUtil.startPage(pageIndex, collect);


        return stringObjectMap;
    }


    public ActivitiHighLineBO queryActivity(String processInsId) {
        HistoricProcessInstance hisProIns = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInsId).singleResult();
        //System.out.println(hisProIns.getProcessDefinitionName()+" "+hisProIns.getProcessDefinitionKey());
        //===================已完成节点
        List<HistoricActivityInstance> finished = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(processInsId)
                .finished()
                .orderByHistoricActivityInstanceStartTime().asc()
                .list();
        Set<String> highPoint = new HashSet<>();
        finished.forEach(t -> {
            if(!"multiInstanceBody".equals(t.getActivityType())){
                highPoint.add(t.getActivityId());
            }
        });

        //=================待完成节点
        List<HistoricActivityInstance> unfinished = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInsId).unfinished().list();
        Set<String> waitingToDo = new HashSet<>();
        unfinished.forEach(t -> waitingToDo.add(t.getActivityId()));

        //=================iDo 我执行过的
      //  Set<String> iDo = new HashSet<>(); //存放 高亮 我的办理节点
        //List<HistoricTaskInstance> taskInstanceList = historyService.createHistoricTaskInstanceQuery().taskAssignee(SecurityUtils.getUsername()).finished().processInstanceId(processInsId).list();
        //taskInstanceList.forEach(a -> iDo.add(a.getTaskDefinitionKey()));

        //===========高亮线
        Set<String> highLine2 = new HashSet<>(); //保存高亮的连线
        //获取流程定义的bpmn模型
        BpmnModelInstance bpmn = repositoryService.getBpmnModelInstance(hisProIns.getProcessDefinitionId());
        //已完成任务列表 可直接使用上面写过的
        List<HistoricActivityInstance> finishedList = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(processInsId)
                .finished()
                .orderByHistoricActivityInstanceStartTime().asc()
                .list();
        int finishedNum = finishedList.size();
        //循环 已完成的节点
        for (int i = 0; i < finishedNum; i++) {
            HistoricActivityInstance finItem = finishedList.get(i);
            //根据 任务key 获取 bpmn元素
            ModelElementInstance domElement = bpmn.getModelElementById(finItem.getActivityId());
            //转换成 flowNode流程节点 才能获取到 输出线 和输入线
            FlowNode act = (FlowNode)domElement;
            if(act == null ){
                continue;
            }

            Collection<SequenceFlow> outgoing = act.getOutgoing();
            //循环当前节点的 向下分支
            int k = i;
            //遍历这个节点的输出箭头
            outgoing.forEach(v->{


                //从当前节点 遍历后面的节点
                for (int j = k; j < finishedNum; j++) {

                    HistoricActivityInstance setpFinish = finishedList.get(j);
                    DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                        if((format.format(finItem.getEndTime()).compareTo(format.format(setpFinish.getStartTime()))) ==0 ){
                            ModelElementInstance domElements = bpmn.getModelElementById(setpFinish.getActivityId());
                            //转换成 flowNode流程节点 才能获取到 输出线 和输入线
                            FlowNode acts = (FlowNode)domElements;

                                if(acts == null){
                                    continue;
                                }
                                List<SequenceFlow> collect = acts.getIncoming().stream().collect(Collectors.toList());
                                if(CollectionUtil.isNotEmpty(collect)){
                                    //遍历这个节点的输入箭头
                                    collect.stream().forEach(ll ->{
                                        if(ll.getId().equals(v.getId())){
                                            highLine2.add(v.getId());
                                        }
                                    });

                                }



                        }
                }
                //待完成
//                for (int j = 0; j < unfinished.size(); j++) {
//                    //循环待节点 和当前完成节点的向下分支比对
//                    HistoricActivityInstance setpUnFinish = unfinished.get(j);
//                    String finxId = setpUnFinish.getActivityId();
//                    if(tarId.equals(finxId)){
//                        if(finItem.getEndTime().equals(setpUnFinish.getStartTime())){
//                            highLine2.add(v.getId());
//                        }
//                    }
//                }

            });
        }

        //返回结果
        ActivitiHighLineBO activitiHighLineDTO =new ActivitiHighLineBO();
        activitiHighLineDTO.setHighPoint(highPoint);
        activitiHighLineDTO.setHighLine(highLine2);
       // activitiHighLineDTO.setWaitingToDo(waitingToDo);
        return activitiHighLineDTO;
    }

}
